"""
server level message implementations go here


@todo: add docstrings and move the stubs below to correct files

"""

from jackson import debug, utils

debug.pre_import(__name__)
from jackson.chord import db
from jackson.rttopt import net
from jackson.rttopt.db import lookup_IP_table, update_IP_table, create_IP_table, minimum_RTT_from_IP_table
from jackson.cdn.core import i_am_responsible_for, get_url
from jackson.cdn.client_api import get_key_status
from jackson.cdn.models import ContentEntry
from models import LeaveRecord
from random import choice

debug.post_import(__name__)

UPDATE_DEBUG_LEVEL = 1
LOOKUP_DEBUG_LEVEL = 1

def lookup_handler(key):
    """ return the fastest server we know about, for the key we're given.
    The key we're given is probably the hash for a ciderization of some IP.

    @rtype: a dictionary. it might contain
        -"rtt"     : rtt for this server
        -"server"  : a Server object representing the fastest server
        -"redirect": a string representing the url of a server who has the 
                     information needed
        -"explore" : boolean, indicating whether we should redirect the client 
                     to a random node
    Procees an RTT lookup request
    """
    context = [__name__, "lookup_handler()"]
    debug.enter(context,None,LOOKUP_DEBUG_LEVEL)
    d = {}
    s = get_key_status(key)
    if i_am_responsible_for(key):
        if s == "H":
            debug.comment(context, "cdn says the file is here,just lookup",
                    LOOKUP_DEBUG_LEVEL)
            try:
                debug.comment(context,"calling into rttopt.db.lookup_IP_table()")
                (serv,rtt) = minimum_RTT_from_IP_table(key)
                d['server'] = serv
                d['rtt'] = rtt
            except Exception, e:
                debug.exception(context,"lookup_handler() issue: "+str(e))
                debug.comment(context,"cdn problem, the file's not here")
        elif s == "O":
            debug.comment(context,
                    "contradiction: i'm responsible and the file is marked as outgoing",
                    LOOKUP_DEBUG_LEVEL)
        else:
            # i'm responsible, file's not here
            new_url = get_url(key)
            debug.comment(context,"redirecting to " + new_url + 
                          ". i'm responsible and the file isn't here yet.",
                          LOOKUP_DEBUG_LEVEL)            
            d['redirect'] = new_url
    else:
        # i'm not responsible for the key. 
        # maybe it's on it's way out, but i should serve the request anyways.
        if s == "H":
            debug.comment(context,
                    "contradiction: i'm not responsible and the file is 'H'")
        elif s == "O":
            debug.comment(context,
                    "the file is here, just perform the lookup (load the database?)",
                    LOOKUP_DEBUG_LEVEL)
            debug.comment(context,"(we aren't responsible) calling into lookup_IP_table")
            (serv,rtt) = minimum_RTT_from_IP_table(key)
            d['server'] = serv
            d['rtt'] = rtt
        else:
            # i'm not responsible, and the file's not here. redirect to successor(key)
            debug.comment(context,"adding where the key is stored")
            new_url = get_url(key)
            debug.comment(context,"redirecting to " + str(new_url) + 
                          ". they should have the table we need, or know where to find it",
                          LOOKUP_DEBUG_LEVEL)
            d['redirect'] = new_url
    debug.leave(context,{'answer dict':d},LOOKUP_DEBUG_LEVEL)
    return d

def update_handler(key,server,rtt,cstr):
    """
    Process an RTT update request.
    """
    context = [__name__, "update_handler()"]
    debug.enter(context,None,UPDATE_DEBUG_LEVEL)
    return_value = False
    s = get_key_status(key)
    if i_am_responsible_for(key):
        if s not in ["H", "O"]:
            # file doesn't yet exist
            debug.comment(context,"creating and updating a new table.",UPDATE_DEBUG_LEVEL) 
            try:
                debug.comment(context,"calling into rttopt.db.create_IP_table, then update")
                create_IP_table(key,cstr)
                update_IP_table(key,server,rtt)
                return_value = True
            except Exception, e:
                debug.exception(context,str(e))                
        else:
            # the file is here. print the status, then perform an update on the table
            if s == "H":
                debug.comment(context,"according to cdn: the file is here",UPDATE_DEBUG_LEVEL)
            elif s == "O":
                debug.comment(context,"according to cdn: file is outgoing",UPDATE_DEBUG_LEVEL)
            else:
                debug.comment(context,"unknown case",UPDATE_DEBUG_LEVEL)
            update_IP_table(key,server,rtt)
            return_value = True
    else:
        # i'm not responsible for the key. 
        if s == "H":
            debug.comment(context,"why is the file here? this is bad",UPDATE_DEBUG_LEVEL)
        elif s == "O":
            debug.comment(context,"in the middle of transferring the file",UPDATE_DEBUG_LEVEL)
            update_IP_table(key,server,rtt)
            return_value = True
        else:
            debug.comment(context,"unknown case",UPDATE_DEBUG_LEVEL)
    debug.leave(context,{'return_value':return_value},UPDATE_DEBUG_LEVEL)
    return return_value

def leave_handler(leaving_server, timestamp):
    """
    leave the network. initiate a broadcasted wave of leave messages
    @note: this may be a different server, or ourself.
    @return: True if we forward the message, false if it is old
    """
    context = [__name__, "leave_handler()"]
    debug.enter(context,None)
    # if we already have this message, just ignore it.
    # otherwise save it to the database and forward it
    try:
        match = LeaveRecord.objects.get(timestamp = timestamp)
        debug.comment(context,"found a matching record, ignoring it.")
        ignore = True
    except:
        debug.comment(context,"passing this message on.")
        ignore = False
    if ignore:
        r = False
    else:
        rec = LeaveRecord(timestamp = timestamp, ip = leaving_server.ip, 
                          port = leaving_server.port)
        rec.save()
        # tell everyone we know
        net.leave(leaving_server,timestamp)
        r = True
    debug.leave(context,{"r":r})
    return r

def leave_net():
    """ invoke a leave (this server is leaving)
    (this is probably an admin view. )
    """
    context = [__name__, "leave_net()"]
    debug.enter(context,None)
    debug.comment(context,"creating the LeaveRecord. saving to the database")
    me = db.get_me()
    now = utils.timestamp()
    # create an entry in my database for my leave, so i don't process it again
    rec = LeaveRecord(timestamp = now, ip = me.ip, port=me.port)
    rec.save()
    debug.comment(context,"sending off leaves")
    try:
        net.leave(me,now)
    except Exception, e:
        debug.exception(context,"some sort of issue here" + str(e))
    debug.leave(context,None)
